package com.oujia.cloud.cloudcommon.bean.config;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Component
public class RedisManager {

    private static RedisManager redisManager;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @PostConstruct
    private void init() {
        redisManager = this;
    }

    /**
     * 设置key指向的数据的过期时长
     *
     * @param key
     * @param expire
     */
    public static void expire(String key, long expire) {
        redisManager.stringRedisTemplate.expire(key, expire, TimeUnit.SECONDS);
    }

    /**
     * 设置key指向的数据的过期时长
     *
     * @param key
     */
    public static Long getExpire(String key) {
        return redisManager.stringRedisTemplate.getExpire(key);
    }

    /**
     * 从redis中移除key对应的value
     *
     * @param key
     */
    public static void delete(String key) {
        redisManager.stringRedisTemplate.delete(key);
    }

    /**
     * 检查给定 key 是否存在（底层：若 key 存在，返回 1 ，否则返回 0 。）
     *
     * @param key
     * @return
     */
    public static Boolean hasKey(String key) {
        return redisManager.stringRedisTemplate.hasKey(key);

    }

    /**
     * redis同步锁
     *
     * @param key
     * @return
     */
    public static Boolean setIfAbsent(String key, String value, long expire) {
        if (redisManager.stringRedisTemplate.opsForValue().setIfAbsent(key, value)) {
            redisManager.stringRedisTemplate.opsForValue().set(key, value, expire, TimeUnit.SECONDS);
            return true;
        } else {
            redisManager.stringRedisTemplate.opsForValue().set(key, value, expire, TimeUnit.SECONDS);
        }
        return false;
    }
    /************************************************************************************/
    /********************************Data-Type: String***********************************/
    /************************************************************************************/
    public static void set(String key, String value) {
        redisManager.stringRedisTemplate.opsForValue().set(key, value);
    }

    public static void set(String key, String value, long expire) {
        redisManager.stringRedisTemplate.opsForValue().set(key, value, expire, TimeUnit.SECONDS);
    }
    public static void set(String key, Object value) {
        redisManager.stringRedisTemplate.opsForValue().set(key, JSON.toJSONString(value));
    }

    public static void set(String key, Object value, long expire) {
        redisManager.stringRedisTemplate.opsForValue().set(key, JSON.toJSONString(value), expire, TimeUnit.SECONDS);
    }

    public static String get(String key) {
        return redisManager.stringRedisTemplate.opsForValue().get(key);
    }

    public static <T> T get(String key, Class<T> clazz) {
        String value = redisManager.stringRedisTemplate.opsForValue().get(key);
        if (value == null) {
            return null;
        }
        return JSON.parseObject(value, clazz);
    }

    /************************************************************************************/
    /***************************Data-Type: ZSet（有序集合）*******************************/
    /************************************************************************************/
    public static void zAdd(String key, String value) {
        redisManager.stringRedisTemplate.opsForZSet().add(key, value, 0.0D);
    }

    public static void zAdd(String key, Object value) {
        redisManager.stringRedisTemplate.opsForZSet().add(key, JSON.toJSONString(value), 0.0D);
    }

    public static void zAdd(String key, String value, Long score) {
        redisManager.stringRedisTemplate.opsForZSet().add(key, value, score);
    }

    public static void zAdd(String key, Collection<?> c, Class<?> clazz) {
        Set<ZSetOperations.TypedTuple<String>> values = new HashSet<>();

        Iterator iterator = c.iterator();
        while (iterator.hasNext()) {
            String element = null;

            if (clazz == String.class) {
                element = (String) iterator.next();
            } else {
                element = JSON.toJSONString(iterator.next());
            }

            ZSetOperations.TypedTuple<String> tuple = new DefaultTypedTuple<String>(element, 0.0D);
            values.add(tuple);
        }

        redisManager.stringRedisTemplate.opsForZSet().add(key, values);
    }

    public static <T> List<T> zRange(String key, int start, int end, Class<T> clazz) {
        Set<String> set = redisManager.stringRedisTemplate.opsForZSet().range(key, start, end);
        return toList(set, clazz);
    }

    /**
     * 排序
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public static Set<String> reverseRange(String key, int start, int end) {
        Set<String> set = redisManager.stringRedisTemplate.opsForZSet().reverseRange(key, start, end);
        return set;
    }

    public static Long zCard(String key) {
        return redisManager.stringRedisTemplate.opsForZSet().zCard(key);
    }

    public static Long zRem(String key, String... values) {
        return redisManager.stringRedisTemplate.opsForZSet().remove(key, values);
    }

    public static Long zRank(String key, Object value) {
        return redisManager.stringRedisTemplate.opsForZSet().rank(key, value);
    }

    /************************************************************************************/
    /****************************Data-Type: Set（无序集合）*******************************/
    /************************************************************************************/
    public static Long sAdd(String key, String... value) {
        return redisManager.stringRedisTemplate.opsForSet().add(key, value);
    }

    public static Set<String> sMembers(String key) {
        return redisManager.stringRedisTemplate.opsForSet().members(key);
    }

    public static Long sCard(String key) {
        return redisManager.stringRedisTemplate.opsForSet().size(key);
    }

    public static Long sRem(String key, String... values) {
        return redisManager.stringRedisTemplate.opsForSet().remove(key, values);
    }

    public static Boolean sIsMember(String key, Object value) {
        return redisManager.stringRedisTemplate.opsForSet().isMember(key, value);
    }

    /************************************************************************************/
    /********************************Data-Type: hashes***********************************/
    /************************************************************************************/
    public static void hSet(String key, String field, String value) {
        redisManager.stringRedisTemplate.opsForHash().put(key, field, value);
    }

    public static void hSet(String key, String field, Object value) {
        redisManager.stringRedisTemplate.opsForHash().put(key, field, JSON.toJSONString(value));
    }

    public static String hGet(String key, String field) {
        return (String) redisManager.stringRedisTemplate.opsForHash().get(key, field);
    }

    public static <T> T hGet(String key, String field, Class<T> clazz) {
        String value = (String) redisManager.stringRedisTemplate.opsForHash().get(key, field);
        if (value == null) {
            return null;
        }
        return JSON.parseObject(value, clazz);
    }

    public static Map hGetAll(String key) {
        return (Map) redisManager.stringRedisTemplate.opsForHash().entries(key);
    }

    public static <T> Map<String, T> hGetAll(String key, Class<T> clazz) {
        Map<String, String> result = hGetAll(key);
        if (result.isEmpty()) {
            return Collections.emptyMap();
        } else {
            Map<String, T> newMap = new HashMap(result.size());
            result.forEach((k, v) -> {
                newMap.put(k, JSON.parseObject(v, clazz));
            });
            return newMap;
        }
    }

    public static List<String> hFields(String key) {
        Set set = redisManager.stringRedisTemplate.opsForHash().keys(key);
        return toList(set, String.class);
    }

    /**
     * redis数据结构：hashes[获取所有的Value值，并返回一个List，这里针对String类型]
     *
     * @param key
     * @return
     */
    public static List hMultiGet(String key) {
        Set set = redisManager.stringRedisTemplate.opsForHash().keys(key);
        List list = redisManager.stringRedisTemplate.opsForHash().multiGet(key, set);
        return list;
    }

    /**
     * redis数据结构：hashes[获取所有的Value值，并返回一个List，这里针对Object类型]
     *
     * @param key
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> List<T> hMultiGet(String key, Class<T> clazz) {
        List<String> result = hMultiGet(key);
        if (result.isEmpty()) {
            return Collections.emptyList();
        } else {
            List<T> newList = new ArrayList(result.size());
            result.forEach(x -> {
                newList.add(JSON.parseObject(x, clazz));
            });
            return newList;
        }
    }

    public static void hDel(String key, String... field) {
        redisManager.stringRedisTemplate.opsForHash().delete(key, field);
    }

    /************************************************************************************/
    /**********************************Data-Type: List***********************************/
    /************************************************************************************/
    public static Long lPush(String key, String value) {
        return redisManager.stringRedisTemplate.opsForList().leftPush(key, value);
    }

    public static Long lPush(String key, Object value) {
        return redisManager.stringRedisTemplate.opsForList().leftPush(key, JSON.toJSONString(value));
    }

    public static String lIndex(String key, Long index) {
        return redisManager.stringRedisTemplate.opsForList().index(key, index);
    }

    public static <T> T lIndex(String key, Long index, Class<T> clazz) {
        String value = lIndex(key, index);
        if (value == null) {
            return null;
        }
        return JSON.parseObject(value, clazz);
    }

    public static <T> List<T> lRange(String key, int start, int end, Class<T> clazz) {
        List<String> list = redisManager.stringRedisTemplate.opsForList().range(key, start, end);
        return toList(list, clazz);

    }

    /********************************工具类***********************************/

    private static <T> List<T> toList(Collection<String> value, Class<T> clazz) {
        if (value == null) {
            return null;
        } else if (clazz == String.class) {
            return new ArrayList(value);
        } else {
            List<T> newValue = Lists.newArrayList();
            Iterator iterator = value.iterator();

            while (iterator.hasNext()) {
                String temp = (String) iterator.next();
                newValue.add(JSON.parseObject(temp, clazz));
            }

            return newValue;
        }
    }
}
